package com.lemon.boot.common.listener;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.lemon.boot.common.annotation.NotAuthorization;
import com.lemon.boot.common.entity.InterfaceInfo;
import com.lemon.boot.common.entity.MethodInfo;
import com.lemon.boot.common.utils.SecurityUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.lang.annotation.Annotation;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 李猛
 * @datetime 2024/3/21 18:58
 * @description 项目启动时执行-获取所有的接口路径
 */
@Slf4j
@Component
public class ApplicationStartEventListener {
    //1.定义所有的接口路径，不包括带有权限注解的接口
    private List<InterfaceInfo> allInterface = new ArrayList<>();

    /**
     * 项目启动时执行-获取所有的接口路径，不包含 knife4j
     */
    @EventListener(ApplicationStartedEvent.class)
    public void onInitAllInterfaces() {
        //1.定义所有的菜单资源路径
        List<InterfaceInfo> interfaceInfoList = new ArrayList<>();

        //2.获取所有控制器bean的名称
        String[] controllerBeanNames = SpringUtil.getApplicationContext().getBeanNamesForAnnotation(Controller.class);

        //3.过滤掉 knife4j 的接口后，循环所有的bean
        for (String name : controllerBeanNames) {
            //3.1.获取类对象
            Class<?> beanClass = AopProxyUtils.ultimateTargetClass(SpringUtil.getBean(name));

            //3.2.获取类对象上的注解RequestMapping
            RequestMapping classRequestMapping = beanClass.getAnnotation(RequestMapping.class);

            //3.3.获取 RequestMapping注解里的路径
            getRequestPath(classRequestMapping).forEach(classPath -> {
                //3.3.1.方法信息
                List<MethodInfo> methodInfoList = new ArrayList<>();

                //3.3.2.获取所有的方法
                Arrays.stream(beanClass.getMethods()).forEach(method -> {
                    //3.3.2.1.获取方法上的注解
                    Annotation[] annotations = method.getAnnotations();

                    Arrays.stream(annotations)
                            .filter(ApplicationStartEventListener::hasRequestAnnotation)
                            .forEach(annotation -> getRequestPath(annotation)
                                    .forEach(methodPath -> methodInfoList
                                            .add(new MethodInfo()
                                                    .setPath(methodPath)
                                                    .setHasSecurityAnnotation(SecurityUtil.isSecurityAnnotated(annotations))
                                                    .setAuthorization(!method.isAnnotationPresent(NotAuthorization.class)))));
                });

                //3.3.3.添加到结果集
                interfaceInfoList.add(new InterfaceInfo()
                        .setMethodList(methodInfoList)
                        .setPath(classPath)
                        .setHasSecurityAnnotation(SecurityUtil.isSecurityAnnotated(beanClass.getAnnotations()))
                        .setAuthorization(!beanClass.isAnnotationPresent(NotAuthorization.class)));
            });
        }
        this.allInterface = interfaceInfoList;
    }

    /**
     * 获取 RequestMapping 里的路径
     *
     * @param annotation
     * @return
     */
    private static List<String> getRequestPath(Annotation annotation) {
        if (annotation == null) {
            return new ArrayList<>(0);
        }
        if (annotation instanceof RequestMapping) {
            return Arrays.stream(ArrayUtil.addAll(((RequestMapping) annotation).value(), ((RequestMapping) annotation).path())).filter(StringUtils::hasText).distinct().collect(Collectors.toList());
        }
        if (annotation instanceof GetMapping) {
            return Arrays.stream(ArrayUtil.addAll(((GetMapping) annotation).value(), ((GetMapping) annotation).path())).filter(StringUtils::hasText).distinct().collect(Collectors.toList());
        }
        if (annotation instanceof PostMapping) {
            return Arrays.stream(ArrayUtil.addAll(((PostMapping) annotation).value(), ((PostMapping) annotation).path())).filter(StringUtils::hasText).distinct().collect(Collectors.toList());
        }
        if (annotation instanceof PutMapping) {
            return Arrays.stream(ArrayUtil.addAll(((PutMapping) annotation).value(), ((PutMapping) annotation).path())).filter(StringUtils::hasText).distinct().collect(Collectors.toList());
        }
        if (annotation instanceof DeleteMapping) {
            return Arrays.stream(ArrayUtil.addAll(((DeleteMapping) annotation).value(), ((DeleteMapping) annotation).path())).filter(StringUtils::hasText).distinct().collect(Collectors.toList());
        }
        return new ArrayList<>(0);
    }

    /**
     * 判断是否包含注解
     *
     * @param annotation
     * @return
     */
    private static boolean hasRequestAnnotation(Annotation annotation) {
        return annotation instanceof RequestMapping ||
                annotation instanceof GetMapping ||
                annotation instanceof PutMapping ||
                annotation instanceof PostMapping ||
                annotation instanceof DeleteMapping;
    }

    /**
     * 获取路径
     *
     * @param path
     * @return
     */
    private static String getPath(String path) {
        String startPath = path.startsWith("/") ? path : "/" + path;
        return startPath.replaceAll("\\{.*?}", "**");
    }

    /**
     * 获取所有的路径
     *
     * @return
     */
    public List<InterfaceInfo> getAllPath() {
        if (CollectionUtils.isEmpty(allInterface)) {
            onInitAllInterfaces();
        }
        return allInterface;
    }

    /**
     * 获取所有的路径，不包括带有权限注解的接口
     *
     * @return
     */
    public Map<String, List<String>> getNotSecurityPathToMap() {
        return getAllPath().stream()
                .filter(item -> !item.isHasSecurityAnnotation())
                .filter(item -> !CollectionUtils.isEmpty(item.getMethodList()))
                .collect(Collectors.toMap(
                        item -> getPath(item.getPath()),
                        item -> item.getMethodList()
                                .stream()
                                .filter(path -> !path.isHasSecurityAnnotation())
                                .map(path -> getPath(path.getPath()))
                                .collect(Collectors.toList())));
    }

    /**
     * 获取不需要认证的接口
     *
     * @return
     */
    public String[] getNotAuthorizationPath() {
        //1.初始化不需要认证的接口
        Set<String> notAuthorizationList = new HashSet<>();

        //2.循环所有接口
        for (InterfaceInfo face : getAllPath()) {
            //3.如果接口需要认证，就循环方法
            if (face.isAuthorization()) {
                notAuthorizationList.addAll(face.getMethodList()
                        .stream()
                        .filter(item -> !item.isAuthorization())
                        .map(item -> String.format("%s%s", getPath(face.getPath()), getPath(item.getPath())))
                        .collect(Collectors.toList()));
            } else {
                notAuthorizationList.addAll(face.getMethodList()
                        .stream()
                        .map(item -> String.format("%s%s", getPath(face.getPath()), getPath(item.getPath())))
                        .collect(Collectors.toList()));
            }
        }

        //4.判空处理
        if (CollectionUtils.isEmpty(notAuthorizationList)) {
            return new String[0];
        }
        return notAuthorizationList.toArray(new String[0]);
    }
}
